#ifndef JCEWRITER_H
#define JCEWRITER_H

#include <limits>
#include "datawriter.h"

class JceWriter : public DataWriter
{
public:
    enum class JceDataType:qint8{
        BYTE = 0,
        DOUBLE = 5,
        FLOAT = 4,
        INT = 2,
        //JCE_MAX_STRING_LENGTH = 104857600,
        LIST = 9,
        LONG = 3,
        MAP = 8,
        SHORT = 1,
        SIMPLE_LIST = 13,
        STRING1 = 6,
        STRING4 = 7,
        STRUCT_BEGIN = 10,
        STRUCT_END = 11,
        ZERO_TYPE = 12
    };
public:
    JceWriter() = delete;
    JceWriter(QSharedPointer<QByteArray> output = nullptr);
    JceWriter & setServerEncoding(bool isGBK = true);
    JceWriter & writeHead(JceDataType type, int tag);
    JceWriter & writeInt(int tag,int data);
    JceWriter & writeByte(int tag,qint8 data);
    JceWriter & writeShort(int tag,short data);
    JceWriter & writeLong(int tag,qint64 data);
    JceWriter & writeNull(int tag);
    JceWriter & writeBytes(int tag,const QByteArray & data);
    JceWriter & writeString(int tag,const QString & data);
    JceWriter & writeFloat(int tag,float data);
    JceWriter & writeDouble(int tag,double data);
    JceWriter & writeStruct(int tag,JceWriter & data);
    QSharedPointer<QByteArray> getData();
    template<typename T>
    JceWriter & writeList(int tag,const QList<T> & data);
    template<typename TKEY,typename TVALUE>
    JceWriter & writeMap(int tag,const QMap<TKEY,TVALUE> & data);

    virtual JceWriter & serialize(){return *this;};
    //the function can be override by subclass
    //then we can use template method to generate it automatically
    //WARN call this twice may cause wrong

    inline JceWriter & write(int tag,int data){return JceWriter::writeInt(tag,data);}
    inline JceWriter & write(int tag,qint8 data){return JceWriter::writeByte(tag,data);}
    inline JceWriter & write(int tag,short data){return JceWriter::writeShort(tag,data);}
    inline JceWriter & write(int tag,qint64 data){return JceWriter::writeLong(tag,data);}
    inline JceWriter & write(int tag){return JceWriter::writeNull(tag);}
    inline JceWriter & write(int tag,const QByteArray & data){return JceWriter::writeBytes(tag,data);}
    inline JceWriter & write(int tag,const QString & data){return JceWriter::writeString(tag,data);}
    inline JceWriter & write(int tag,float data){return JceWriter::writeFloat(tag,data);}
    inline JceWriter & write(int tag,double data){return JceWriter::writeDouble(tag,data);}
    inline JceWriter & write(int tag,JceWriter & data){return JceWriter::writeStruct(tag,data);}
    template<typename TKEY,typename TVALUE>
    inline JceWriter & write(int tag,const QMap<TKEY,TVALUE> & data){return JceWriter::writeMap(tag,data);}
    template<typename T>
    inline JceWriter & write(int tag,const QList<T> & data){return JceWriter::writeList<T>(tag,data);}
    template<typename T>
    inline JceWriter & writeT(int tag,T & data){return JceWriter::write(tag,data);}
private:
    bool sServerEncodingGBK = true;//UTF-8
    static int JCE_MAX_STRING_LENGTH;
};

template<typename T>
JceWriter & JceWriter::writeList(int tag,const QList<T> & data){
    //if(data.size() == 0)return writeNull(tag);
    writeHead(JceDataType::LIST,tag);
    JceWriter::writeInt(0,data.size());
    foreach (auto item, data) {//TODO REFERENCE
        JceWriter::writeT<T>(0,item);
    }
    return *this;
}

template<typename TKEY,typename TVALUE>
JceWriter & JceWriter::writeMap(int tag,const QMap<TKEY,TVALUE> & data){
    //if(data.size() == 0)return writeNull(tag);
    writeHead(JceDataType::MAP,tag);
    JceWriter::writeInt(0,data.size());
    foreach (auto item, data.keys()) {
        JceWriter::writeT<TKEY>(0,item);
        TVALUE tmp = data[item];
        JceWriter::writeT<TVALUE>(1,tmp);
        //if you want to call write
        //you must convert the data type by yourself
    }
    return *this;
}

#endif // JCEWRITER_H
